﻿using namespace System.Collections.Generic;

[AttributeUsage([AttributeTargets]::Method,AllowMultiple=$false,Inherited=$true)]
class Test : Attribute
{
    Test()
    {
    }
}

[AttributeUsage([AttributeTargets]::Method,AllowMultiple=$false,Inherited=$true)]
class SetUp : Attribute
{
    SetUp()
    {
    }
}

[AttributeUsage([AttributeTargets]::Method,AllowMultiple=$false,Inherited=$true)]
class TearDown : Attribute
{
    TearDown()
    {
    }
}

class UnitTestException:ApplicationException
{

}

class ErrorMsgList:List[string]
{
}

class TestBase
{
    [object]$target;
    [ErrorMsgList]$msgList;

    TestBase()
    {
        $ErrorActionPreference = "Stop"
        $Error.Clear();
    }

    [void]RunTest()
    {
        $error.Clear()
        $mytype = $this.GetType();
        $memberList  = $mytype.GetMethods();

        $setUp = $null;
        $tearDown = $null;

        $testMemberList = New-Object System.Collections.ArrayList

        foreach($member in $memberList )
        {
            $att=[Attribute]::GetCustomAttributes($member)

            if($att -ne $null -and $att.Count -ge 1 ) 
            {
                $name = $att[0].GetType().Name
                if( $name -eq "SetUp" )
                {
                    $setUp = $member;
                }
                elseif( $name -eq "TearDown" )
                {
                    $tearDown = $member;
                }
                elseif( $name -eq "Test" )
                {
                    [void]$testMemberList.Add($member);
                }
            }
        }

        $succeedCount = 0
        $errorCount = 0
        $No = 1
        foreach($testMember in $testMemberList )
        {
            $name = ""
            try
            {
                if( $setUp -ne $null )
                {
                    [void]$this."SetUp"();
                }

                $this.msgList = [ErrorMsgList]::new();

                $name = $testMember.Name
                [void]$this."$name"();

                if( $tearDown -ne $null )
                {
                    [void]$this."TearDown"();
                }

                Write-Host ("[{0}]OK:{1}" -F $No,$name ) -ForegroundColor Green
                $succeedCount += 1
            }
            catch [Exception]
            {
                Write-Host ( "[{0}]NG:{1}" -F $No,$name )  -ForegroundColor Red
                foreach($msg in $this.msgList)
                {
                    Write-Host $msg -ForegroundColor Red
                }

                if( $_.Exception -ne $null )
                {
                    if( $_.Exception.GetType().Name -ne 'UnitTestException' )
                    {
                        Write-Host ( " + Exception: {0}" -F $_.Exception.Message )  -ForegroundColor Red
                    }
                }
                $errorCount += 1
            }
            finally
            {
                $No += 1;
            }
        }

        if( $errorCount -eq 0 )
        {
            Write-Host ("`nResult:OK={0}" -F $succeedCount ) -ForegroundColor Green
        }
        else
        {
            Write-Host ("`nResult:NG={0}:OK={1}" -F $errorCount,$succeedCount ) -ForegroundColor Red
        }
    }

    [bool]AssertEquals([object]$expected,[object]$actual,[string]$message)
    {
        if($expected.Equals($actual ) -ne $true )
        {
            $this.msgList.Add( " +  AssertEquals Fail" )
            $this.msgList.Add( " +   Message : $message" )
            $this.msgList.Add( " +   Expected: $expected" )
            $this.msgList.Add( " +   Actual  : $actual" )
            throw ( [UnitTestException]::new() )
        }
        return $true;
    }

    [bool]AssertEquals([object]$expected,[object]$actual)
    {
        return $this.AssertEquals( $expected, $actual,"Test failed." ) 
    }

    [bool]Assert( [bool] $condition, [string] $message )
    {
        if( $condition -eq $false )
        {
            $this.msgList.Add( " +  Assert Fail" )
            $this.msgList.Add( " +   Message: $message" )
            throw ( [UnitTestException]::new() )
        }
        return $true;
    }
    [bool]Assert( [bool] $condition )
    {
        return $this.Assert( $condition, "Test failed." );
    }

}

<#
# Test

class TestTarget
{
    [int]Add([int]$a,[int]$b)
    {
        Write-host "TestTarget:Add"
        return $a + $b;
    }
}


class TestSample : TestBase
{
    TestSample()
    {
        Write-host "TestSample:TestSample"
        $this.target = [TestTarget]::new();
    }

    [void]SetUp()
    {
        Write-host "TestSample:SetUp"
        $this.target = [TestTarget]::new();
    }

    [void]TearDown()
    {
        Write-host "TestSample:TearDown"
        $this.target = $null;
    }

    [Test()]
    [void]TestAdd1()
    {
        Write-host "TestSample:TestAdd1"
        $this.AssertEquals( 3, $this.target.Add(1,2), "Error TestAdd1" );
    }

    [void]TestAdd2()
    {
        Write-host "TestSample:TestAdd2"
        $this.AssertEquals( 2, $this.target.Add(2,0), "Error TestAdd2" );
    }

    [void]TestAdd3()
    {
        Write-host "TestSample:TestAdd3"
        $this.Assert( $this.target.Add(0,0) -eq 0, "Error Assert Check" );
        $this.Assert( $this.target.Add(1,-1) -eq 0);
    }
}

[void](([TestSample]::new()).RunTest());

#>